# 获取权限用到的a
# 获取Java源码用到的d
from androguard.misc import AnalyzeAPK
# 加载父目录
from pathlib import Path
# 获取dex用到的
import androguard.core.apk
import numpy as np
from PIL import Image
import struct
import math
from django.core.files.base import ContentFile
import io
from apk.models import *


# ---------------------------------处理dex文件PK_dexInfo表---------------------------------
# 处理二进制序列并转换为RGB值
def process_binary_to_rgb(binary_data):
    # 确保数据长度是24的倍数，不足则填充0
    padded_length = (len(binary_data) + 23) // 24 * 24
    padded_data = binary_data.ljust(padded_length, b'\x00')

    # 将24位二进制序列分割成RGB值
    rgb_values = []
    for i in range(0, len(padded_data), 3):
        r, g, b = struct.unpack('BBB', padded_data[i:i + 3])  # struct.unpack()是一个用于解析字节串为Python数据类型的函数
        # 参数为一个格式字符串和一个字节串，格式字符串描述了如何解析字节串中的数据，'BBB'表示要解析三个无符号的字节
        rgb_values.append((r, g, b))

    return rgb_values

# 将RGB值列表转换为RGB彩色图像
def binary_to_image(rgb_values, width=None, height=None):
    # 如果未指定宽高，则计算它们
    if width is None or height is None:
        pixels_num = len(rgb_values)  # 因为每个像素有三个值（RGB）
        # 尝试找到一个尽可能接近的平方数
        approx_square = int(math.sqrt(pixels_num))

        # 从最大的可能宽度开始尝试，直到找到一个合适的宽度和高度
        for possible_width in range(approx_square, 1, -1):
            if pixels_num % possible_width == 0:
                height = pixels_num // possible_width
                width = possible_width
                break
        else:
            # 如果没有找到合适的宽度（这通常不会发生，除非像素数太少），则使用最接近的平方根作为宽度和高度
            width, height = approx_square, approx_square

    # 转换为NumPy数组并重塑为图像尺寸
    rgb_array = np.array(rgb_values, dtype=np.uint8).reshape((height, width, 3))

    # 转换为PIL图像
    image = Image.fromarray(rgb_array, 'RGB')
    return image

# 彩色图像正方形化（0填充）
def square_image(image):
    width, height = image.size
    # 计算正方形尺寸
    side_length = max(width, height)
    # 创建一个新的正方形图像并用黑色填充
    new_image = Image.new('RGB', (side_length, side_length), color=(0, 0, 0))
    # 将原始图像粘贴到新的正方形图像上
    new_image.paste(image, ((side_length - width) // 2, (side_length - height) // 2))
    return new_image

def get_java_code(d):
    java_code = ""
    for dex in d:
        for class_def in dex.get_classes():
            smali_class = class_def.get_source()
            class_name = class_def.get_name()
            java_code += class_name + "\n" + smali_class + "\n\n"
    text_io_wrapper = io.StringIO(java_code)
    # 读取_io.TextIOWrapper对象的内容
    file_content = text_io_wrapper.getvalue().encode('utf-8')  # 转换为字节串，因为FileField期望的是字节类型的数据
    # 创建一个ContentFile对象
    content_file = ContentFile(file_content, name="app_name" + '.txt')  # 作为文件的名称被保存
    return content_file

def extract_dex_from_apk(a2dex):
    # 加载 APK 文件
    a = a2dex
    # 获取图像名称-app应用名
    try:
        image_name = a.get_app_name()
    except:
        image_name = "app名称获取失败"
    # 获取 APK 中的所有 DEX 文件
    dexs = a.get_all_dex()
    # 遍历所有的dex文件，一起转换为灰度图像
    # 存储所有dex的二进制文件相加得到总的
    binary_data = b''
    for dex in dexs:
        binary_data += dex
    try:
        rgb_values = process_binary_to_rgb(binary_data)
        image = binary_to_image(rgb_values)
        # 取消矩形化操作
        # square_image_result = square_image(image)
        # PIL.Image.Image类型与ImageField类型不能传递，ImageField是二进制数据
        temp_handle = io.BytesIO()
        image.save(temp_handle, 'PNG')
        temp_handle = temp_handle.getvalue()
        print("dexImage图像解析成功")

        # 还有Java源码和相关诈骗站点为加入到其中

        # content_file = get_java_code(d)
        # print("Java源码解析成功")

        # 数据库写入
        Apk_dexInfo = apk_dexInfo.objects.create(dex_image=ContentFile(temp_handle, name=image_name+".png"))
        print("数据库保存成功")
        return Apk_dexInfo
    except Exception as e:
        print("报错了")
        print(e)




# -------------------------处理related_site信息-----------------------------------------

import paramiko

def parse_urls(apk_name):
    # 将pycharm与kali虚拟机连接
    # 连接设置
    ssh_host = '192.168.204.128'  # 替Kali 虚拟机 IP 地址
    ssh_user = 'wzd'  # Kali 用户名
    ssh_password = '20050213w'  # Kali 密码

    # 创建 SSH 连接对象
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    # 连接到 Kali 虚拟机
    try:
        ssh.connect(hostname=ssh_host, username=ssh_user, password=ssh_password)
        print('SSH connection established')
    except Exception as e:
        print(f"连接失败：{e}")
        exit(1)
    # _endpoints.txt //包含具有完整 URL 路径的终结点,更加的详细（选择这个）-写入文件，以文件的形式存储
    # 属于是url
    # 动态解析通联地址主要是域名
    # 文件在kali虚拟机中的路径
    _endpoints_path = '/home/wzd/endpoints/63b_endpoints.txt'

    # 执行解析命令和读取解析后结果的命令
    # 执行解析的命令
    m = "'/mnt/hgfs/apk_file/2c.apk'"
    print(m)
    # stdin, stdout, stderr = ssh.exec_command(f'apk2url ''{_endpoints_path}')
    # 读取解析结果命令
    stdin, stdout, stderr = ssh.exec_command(f'cat {_endpoints_path}')
    output = stdout.readlines()

    # 显示输出结果
    for line in output:
        print(line.strip())

    c = ""
    # 直接经过处理后的临时文件
    return c


if __name__ == '__main__':
    pass



